#!/usr/bin/env python

import os
import sys
import re
import math
from math import exp,log
from db import *
import db
from barchart import *
from dbstat import DbStat
import numpy as np
from os.path import join as joinpath

def usage():
    print '''\
Usage: %s -c <configuration-file>
''' % sys.argv[0]
    sys.exit(1)

def getopts(list, flags):
    import getopt
    try:
        opts, args = getopt.getopt(list, flags)
    except getopt.GetoptError:
        usage()

    return opts, args

work_dir = 'spec_workdir'
isa = 'trips'
inputset = 'ref'
home = os.environ['HOME']

output_dir = os.path.join(home, work_dir, 'figs')

int_benchmarks = ['gzip_graphic','vpr_route','gcc_166','mcf',
                  'perlbmk_makerand',
                  'crafty','parser_spec','gap','vortex_2',
                  'bzip2_program','twolf',]

float_benchmarks = ['wupwise','swim',
                    'mgrid','applu','mesa','art_110',
                    'equake','ammp','apsi'] 

all_benchmarks = int_benchmarks + float_benchmarks

class TopN(object):

    statBaseNameSeq = 'system.cpu.commit.branchTypeTop100SeqPercent' 
    statBaseNameBr = 'system.cpu.commit.branchTypeTop100BranchPercent' 
    statBaseNameCall = 'system.cpu.commit.branchTypeTop100CallPercent' 
    statBaseNameRet = 'system.cpu.commit.branchTypeTop100RetPercent' 

    def __init__(self, source, bname, config, num, **kwargs):
        # Number of hyperblocks 100% dominated by Sequential target
        self.numSeqFullDom = 0
        # Number of hyperblocks 100% dominated by Branch target
        self.numBrFullDom = 0
        # Number of hyperblocks 100% dominated by Call target
        self.numCallFullDom = 0
        # Number of hyperblocks 100% dominated by Ret target
        self.numRetFullDom = 0

        # Number of hyperblocks dominated by Sequential target,
        # percentage of which is more than 50% and less than 100%.
        # All of the Rests are Branch targets. 
        self.numSeqHalfDom = 0
        # Number of hyperblocks dominated by Branch target,
        # percentage of which is more than 50% and less than 100%.
        # All of the Rests are Sequential targets. 
        self.numBrHalfDom = 0

        self.numSeqBr = 0

        # Number of hyperblocks which can not be attributed to the
        # above classes.
        self.numRests = 0

        dist_stat = \
        DbStat(source,
               bname, 
               'system.cpu.commit.branchTypeMaxDist',
               config)

        if not isinstance(dist_stat.data(), FullDist):
            raise AttributeError, "Stat system.cpu.commit.branchTypeMaxDist is not type of dist" 

        total_num_instances = (dist_stat.data()).samples

        if total_num_instances < num:
            self.totalNumInstances = total_num_instances.data()
        else:
            self.totalNumInstances = num

        stat_seq = DbStat(source,
                          bname,
                          'system.cpu.commit.branchTypeTop100SeqPercent',
                          config,
                         )

        seq_data = stat_seq.data()

        stat_br = DbStat(source,
                         bname,
                         'system.cpu.commit.branchTypeTop100BranchPercent',
                         config,
                         )

        br_data = stat_br.data()

        stat_call = DbStat(source,
                          bname,
                          'system.cpu.commit.branchTypeTop100CallPercent',
                          config)

        call_data = stat_call.data()

        stat_ret = DbStat(source,
                         bname,
                         'system.cpu.commit.branchTypeTop100RetPercent',
                         config)

        ret_data = stat_ret.data()

        for i in range(int(self.totalNumInstances)):

            seq = seq_data[i]
            br = br_data[i]
            call = call_data[i]
            ret = ret_data[i]

            if seq == 1.0:
                self.numSeqFullDom += 1
            elif br == 1.0:
                self.numBrFullDom += 1
            elif call == 1.0:
                self.numCallFullDom += 1
            elif ret == 1.0:
                self.numRetFullDom += 1

            elif seq > 0.0 and br > 0.0 \
                    and call == 0.0 and ret \
                    == 0.0:
                self.numSeqBr += 1

#            elif seq > 0.5 and call == 0.0\
#                    and ret == 0.0:
#                self.numSeqHalfDom += 1
#            elif br > 0.5 and call == 0.0\
#                    and ret == 0.0:
#                self.numBrHalfDom += 1
            else:
                self.numRests += 1

    def data(self):
        return [self.numSeqFullDom, self.numBrFullDom,
                self.numCallFullDom, self.numRetFullDom,
                self.numSeqBr, self.numRests]

def GeometryMean(X):

   return exp(sum([log(x) for x in X]) / len(X))

def ArithmaticMean(X):

    return float(sum([float(x) for x in X]) / float(len(X)))

class DrawingOptions(object):
    def __init__(self, conf, **kwargs):

        if len(conf) < 5 or conf[0] != '!':
            self.valid = False
            return

        self.valid = True

        self.mode = conf[1]
        self.statistics = conf[2].split(',')
        self.benchmarkHint = conf[3]
        self.configurations = conf[4].split(',')
        self.comments = []
        self.flags = []
        self.legend = []
        self.xticks = []
        self.xsubticks = None
        self.outputName = None
        self.relative = False
        self.mean = True
        self.kiloInsts = False
        self.pdf = False
        self.cdf = False
        self.subtract = False
        self.ylable = ""
        self.title = ""

        self.drawingDim = 0

        self.benchmarks = []
        if self.benchmarkHint == 'all' :
            self.benchmarks = int_benchmarks + \
                              float_benchmarks + ['Int Mean'] + ['Fp \
                                      Mean']
        elif self.benchmarkHint == 'int':
            self.benchmarks = int_benchmarks + ['Mean']

        if len(conf) > 5:
            self.comments = conf[5].split(',')
            # Last element in comments should be the label of y axis
            self.ylabel = self.comments[-1].replace('_', ' ')

        if len(conf) > 6:
            self.flags = conf[6].split(',')

        if self.mode == 'SSMB':

            self.drawingDim = 2

            if len(self.statistics) != 1:
                self.valid = False
                print 'In SSMB mode, only one statistics is supported ...'
                return

            comment_len = len(self.comments)
            if comment_len >= len(self.configurations):
                self.legend = self.comments[:len(self.configurations)]
            else:
                self.legend = self.configurations

            if comment_len > len(self.configurations):
                self.outputName = self.comments[len(self.configurations)]
            else:
                name = re.sub(':','--',self.statistics[0])
	        name = re.sub('\.','-',name)
                self.outputName = name

            if 'relative' in self.flags:
                self.relative = True

            if 'no_mean' in self.flags:
                self.mean = False

        elif self.mode == 'MSMB':

            self.drawingDim = 3
            self.topN = 0

            comment_len = len(self.comments)

            if self.topN > 0:
                self.legend = self.comments[:6]
            else:
                self.legend = self.comments[1:1 + int(self.comments[0])]

            if len(self.statistics) == 1 and re.match(r'top_\d',\
                    self.statistics[0]):
                self.topN = int(self.statistics[0].split('_')[1])
            elif len(self.legend) <= 1:
                self.valid = False
                print 'In MSMB mode, you should have more than 1 stat ...'
                return

            if self.topN > 0:
                self.outputName = self.comments[6]
            elif comment_len > len(self.statistics):
                self.outputName = self.comments[len(self.legend) + 1]
            else:
                name = re.sub(':','--',self.statistics[0])
	        name = re.sub('\.','-',name)
                self.outputName = name

            if self.topN > 0:
                self.xsubticks = self.comments[8:comment_len - 1]
            elif comment_len >= len(self.statistics) +\
                              len(self.configurations) + 1:
                self.xsubticks = \
                    self.comments[len(self.legend)+2:\
                    comment_len - 1]

            if 'relative' in self.flags:
                self.relative = True

            if 'no_mean' in self.flags:
                self.mean = False

            if 'kilo_insts' in self.flags:
                self.kiloInsts = True

            if 'pdf' in self.flags:
                self.pdf = True

            if 'cdf' in self.flags:
                self.cdf = True

            if 'subtract' in self.flags:
                self.subtract = True

        elif self.mode == 'SMMB':
            pass

        else:
            print 'Unsupported drawing mode[%s] ...' % self.mode
            self.valid = False

    def isValid(self):
        return self.valid

class Options:pass

if __name__ == '__main__':
    import getpass

    source = db.Database()
    source.host = '202.118.229.70'
    source.db = 'pengfei'
    source.user = 'pengfei'
    source.passwd = '83658365yy'

    try:
        source.connect()
    except:
        raise AttributeError, 'Cannot connect to db'

    options = Options()
    options.conf= None
   
    opts, args = getopts(sys.argv[1:],'-c:')

    for o,a in opts:
        if o == '-c':
            options.conf= a

    if not options.conf:
        sys.exit('Where is your configuration file?')

    if not os.path.isdir(output_dir):
        os.makedirs(output_dir)

    print 'Put figures to %s' % output_dir

    for line in open(options.conf) :

        dp = DrawingOptions(line.split())

        if not dp.isValid():
            continue

        chart = BarChart()

        if dp.drawingDim == 3 and dp.mode == 'MSMB':

            # Add to bar for mean values
            if dp.topN > 0:
                data = np.zeros(len(dp.benchmarks) *
                                len(dp.configurations) *
                                6)
            else:
                data = np.zeros(len(dp.benchmarks) *
                                len(dp.configurations) *
                                len(dp.legend))
            
            if dp.topN > 0:
                data.shape = (len(dp.benchmarks),
                              len(dp.configurations),
                              6)
            else:
                data.shape = (len(dp.benchmarks),
                              len(dp.configurations),
                              len(dp.legend))

            chart.legend = dp.legend
            
            stat_base_name = ((dp.statistics[0]).split('::'))[0]

            chart.ylabel = dp.ylabel

            chart.title = dp.title
            chart.xticks = []
            for b in dp.benchmarks:
                chart.xticks.append(b.split('_')[0])

            if dp.xsubticks:
                chart.xsubticks = dp.xsubticks

            print 'Drawing %s for %s...' % (stat_base_name, \
                    dp.benchmarkHint)

            int_len = len(int_benchmarks)
            fp_len = len(float_benchmarks)

            if dp.benchmarkHint == 'all' or \
                    dp.benchmarkHint == 'int':

                for i,bench in enumerate(int_benchmarks):
                    for j,conf in enumerate(dp.configurations):

                        if dp.kiloInsts:
                            commit_insts = \
                            DbStat(source, bench,
                                   'system.cpu.committedInsts_total',
                                   conf)

                        for k, sname in enumerate(dp.statistics):

                            stat = DbStat(source, bench, sname, conf)

                            if dp.pdf:
                                d = stat.pdf()
                            elif dp.cdf:
                                d = stat.cdf()
                            elif dp.topN > 0:
                                topN = TopN(source, bench, conf, dp.topN)
                                top_n = [float(x) / \
                                        float(topN.totalNumInstances)
                                        for x in topN.data()]
                            else:
                                d = stat.data()

                            print 'bench: %s, conf: %s' % (bench, conf)
                            print d

                            if dp.topN > 0:
                                data[i, j, :] = top_n
                            else:
                                data[i,j,k:k+len(d)] = d

#                            if dp.kiloInsts:
#                                data[i,j,k] = data[i,j,k] /\
#                                    (commit_insts.data() / 1000.0)

#                        if dp.subtract:
#                            data[i,j,-1] = \
#                                data[i,j,-1] - \
#                                sum(data[i,j,x] for x in \
#                                        arange(len(data[i,j]) -\
#                                            1))

#                        if conf.split('_')[-2] != 'ei':
#                            data[i,j,1] += data[i,j,0]
#                            data[i,j,0] = 0.0

#            if dp.benchmarkHint == 'all':

#                for i,bench in enumerate(float_benchmarks):
#                    for j,conf in enumerate(dp.configurations):

#                        if dp.kiloInsts:
#                            commit_insts = Stat(bench,
#                                    'system.cpu.committedInsts_total',
#                                    conf,
#                                    'normal')

#                        for k,sname in enumerate(dp.statistics):

#                            if dp.pdf:
#                                stat = Stat(bench, sname, conf, 'pdf')
#                            elif dp.cdf:
#                                stat = Stat(bench, sname, conf, 'cdf')
#                            elif dp.topN > 0:
#                                topN = TopN(bench, conf, dp.topN)
#                                stat = [(float(x) / \
#                                    float(topN.totalNumInstances))\
#                                        for x in topN.data()]
#                            else:
#                                stat = Stat(bench, sname, conf,
#                                        'normal')

#                            if dp.topN > 0:
#                                data[i+int_len, j, :] = stat
#                            else:
#                                data[i+int_len,j,k] = stat.data()

#                            if dp.kiloInsts:
#                                data[i+int_len,j,k] =\
#                                    data[i+int_len,j,k] /\
#                                    (commit_insts.data() / 1000.0)

#                        if dp.subtract:
#                            data[i+int_len,j,-1] = \
#                                data[i+int_len,j,-1] - \
#                                sum(data[i+int_len,j,x] for x in \
#                                        arange(len(data[i+int_len,j]) -\
#                                            1))


            # Find the arithmatic mean
            if dp.benchmarkHint == 'all' or \
               dp.benchmarkHint == 'int':
                mean_pos = int_len
                chart.extra_grp = 1

            if dp.benchmarkHint == 'all':
                mean_pos += fp_len
                chart.extra_grp = 2

            for j,conf in enumerate(dp.configurations):
                if dp.topN > 0:
                    for k in range(6):
                        data[mean_pos, j, k] = \
                                ArithmaticMean(data[:int_len, j, k])
                else:
                    for k,sname in enumerate(dp.legend):
                        if dp.mean:
                            data[mean_pos, j, k] = \
                                ArithmaticMean(data[:int_len, j, k])
                        else:
                            data[mean_pos, j, k] = 0.0

            # Find arithmatic mean
            if dp.benchmarkHint == 'all':
                for j,conf in enumerate(dp.configurations):
                    if dp.topN > 0:
                        for k in range(6):
                            data[int_len+fp_len+1, j, k] = \
                                    ArithmaticMean(data[int_len:int_len+fp_len,\
                                        j, k])
                    else:
                        for k,sname in enumerate(dp.statistics):
                            if dp.mean:
                                data[int_len+fp_len+1, j, k] = \
                                    ArithmaticMean(data[(int_len):(int_len+fp_len),\
                                        j, k])
                            else:
                                data[int_len+fp_len+1, j] = 0.0

            chart.colormap = 'jet'

        chart.data = data
        chart.extra = None

#        chart.xlabel = 'Benchmarks'
	chart.legend_size = 6
	chart.figure_size = [0.05,0.18,0.83,0.65]
	chart.chart_size = [7,2.625]
	chart.xlabel_rotation = 30
        chart.ygrid = True
#        chart.colormap = 'hot'
	chart.graph()

        fname = dp.outputName
	chart.savefig(joinpath(output_dir, 'png', fname + '.png'))
	chart.savefig(joinpath(output_dir, 'pdf', fname + '.pdf'))
	chart.savefig(joinpath(output_dir, 'eps', fname + '.eps'))
	chart.savefig(joinpath(output_dir, 'ps', fname + '.ps'))
	chart.savecsv(joinpath(output_dir, 'csv', fname + '.csv'))

